package com.study.config;

import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContexts;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * @author zhuzi 2022/6/16 14:32
 */
@Configuration
public class RestsConfig {

    //@Bean
    public RestTemplate restTemplate() {
        //SimpleClientHttpRequestFactory  does not support the HTTP PATCH
        //org.springframework.http.client.support.InterceptingHttpAccessor.setRequestFactory
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(5 * 1000);
        factory.setConnectTimeout(10000);
        return new RestTemplate(new BufferingClientHttpRequestFactory(factory));


    }

    @Bean
    public ClientHttpRequestFactory requestFactory() {
        SSLConnectionSocketFactory sslConnFactory = null;
        SSLContext sslContext = null;
        try {
            sslContext = SSLContexts.custom().loadTrustMaterial(null, (x509Certificates, authType) -> true).build();
            sslConnFactory = new SSLConnectionSocketFactory(sslContext,//
                    NoopHostnameVerifier.INSTANCE);
        } catch (Exception e) {
        }
        // 连接池
        HttpClientBuilder builder = HttpClientBuilder.create().disableAutomaticRetries()//默认重试是开启的，建议关闭
                .evictIdleConnections(10, TimeUnit.SECONDS)//空闲连接10分钟关闭
                .setConnectionTimeToLive(20, TimeUnit.SECONDS)//连接的TTL时间
                .setMaxConnTotal(200)//连接池大小
                .setMaxConnPerRoute(20);//每个主机的最大连接数
        if (sslConnFactory != null) {
            builder.setSSLSocketFactory(sslConnFactory);
        }

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(builder.build());
        return new BufferingClientHttpRequestFactory(factory);
    }

    /**
     * 使用OkHttpClient作为底层客户端｜TODO 未验证
     * <a href="https://www.cnblogs.com/hellosyl/p/18054333">RestTemplate+OkHttp3整合（一）</a>
     *
     * @return
     */
    private ClientHttpRequestFactory getClientHttpRequestFactory() {
        // 创建连接池
        ConnectionPool connectionPool = new ConnectionPool(10, 300, TimeUnit.SECONDS);
        // 信任管理
        X509TrustManager trustManager = null;
        SSLContext sslContext = null;
        try {
            trustManager = new X509TrustManager() {
                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
                }

                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
            // 信任任何链接
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
        } catch (Exception e) {
            e.printStackTrace();
        }
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                .connectionPool(connectionPool)//
                .connectTimeout(5, TimeUnit.SECONDS)//
                .readTimeout(5, TimeUnit.SECONDS)//
                .writeTimeout(5, TimeUnit.SECONDS)//
                .hostnameVerifier((hostname, sslSession) -> true)
                .sslSocketFactory(sslContext.getSocketFactory(), trustManager)
                //.sslSocketFactory(sslSocketFactory(), x509TrustManager())
                .retryOnConnectionFailure(true)
                // 设置代理
                // .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
                // 拦截器
                // .addInterceptor()
                .build();
        //BufferingClientHttpRequestFactory｜对于 HttpComponentsClientHttpRequestFactory，使用 BufferingClientHttpRequestFactory 包装是因为它默认不会缓冲请求体
        //对于 OkHttp3ClientHttpRequestFactory，情况有所不同。OkHttp 客户端默认是异步的，并且它有自己的缓冲机制。因此，通常情况下，你不需要使用 BufferingClientHttpRequestFactory 来包装 OkHttp3ClientHttpRequestFactory，因为 OkHttp 本身已经提供了缓冲功能。
        OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory(okHttpClient);
        return factory;
    }

    @Bean
    public X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
    }

    @Bean
    public SSLSocketFactory sslSocketFactory() {
        try {
            //信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        RestTemplate restTemplate = builder.requestFactory(this::requestFactory).setConnectTimeout(Duration.ofSeconds(10)).setReadTimeout(Duration.ofSeconds(10)).build();

        // 设置消息转换，解决中文乱码问题｜使用OKHTTP 播客这么设置的
        for (HttpMessageConverter<?> converter : restTemplate.getMessageConverters()) {
            // 原有的String是ISO-8859-1编码 ，设置为UTF-8
            if (converter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
                break;
            }
        }
        return restTemplate;
    }
}
